#include "CShader.h"
#include <iostream>

bool InitGLSL()
{
	if( !GLEW_VERSION_2_0)
	{
		printf("You must have OpenGL 2.0 support to run this program!\n");
		return false;
	}
	return true;
}

string CShader::LoadTextFile(string strFile)
{
	ifstream fin(strFile.c_str());

	if(!fin)
	{
		printf("didnt find %s shader file\n", strFile.c_str() );
		return "";
	}

	string strLine = "";
	string strText = "";

	// Go through and store each line in the text file within a "string" object
	while(getline(fin, strLine))
	{
		strText = strText + "\n" + strLine;
	}

	fin.close();

	return strText;
}

void CShader::InitShaders(string strVertex, string strFragment)
{
	// These will hold the shader's text file data
	string strVShader, strFShader;

	printf("loading shader file %s\n", strVertex.c_str() );
	printf("loading shader file %s\n", strFragment.c_str() );

	// Make sure the user passed in a vertex and fragment shader file
	if(!strVertex.length() )
	{
		printf("could not load shader file %s\n", strVertex.c_str() );
		return;
	}
	if(!strFragment.length() )
	{
		printf("could not load shader file %s\n", strFragment.c_str() );
		return;
	}

	// If any of our shader pointers are set, let's free them first.
	if(m_hVertexShader || m_hFragmentShader || m_hProgramObject)
	{
		ReleaseShader();
	}

	m_hVertexShader = glCreateShader(GL_VERTEX_SHADER);
	m_hFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

	strVShader = LoadTextFile(strVertex.c_str());
	strFShader = LoadTextFile(strFragment.c_str());

	// Do a quick switch so we can do a double pointer below
	const char *szVShader = strVShader.c_str();
	const char *szFShader = strFShader.c_str();

	// Now this assigns the shader text file to each shader pointer
	glShaderSource(m_hVertexShader, 1, &szVShader, NULL);
	glShaderSource(m_hFragmentShader, 1, &szFShader, NULL);

	GLint VSCompiled, FSCompiled, linked;

	glCompileShader(m_hVertexShader);
	glGetShaderiv(m_hVertexShader, GL_COMPILE_STATUS, &VSCompiled);
	if ( !VSCompiled )
	{
		std::cout << "error when compiling vertex shader: " << strVertex << "\n\n";
		printShaderInfoLog(m_hVertexShader);
	}
	else{
		std::cout << "vertex shader compiled: " << strVertex << "\n";
	}

	glCompileShader(m_hFragmentShader);
	glGetShaderiv(m_hFragmentShader, GL_COMPILE_STATUS, &FSCompiled);
	if ( !FSCompiled )
	{
		std::cout << "error when compiling vertex shader: " << strFragment << "\n\n";
		printShaderInfoLog(m_hFragmentShader);
	}
	else{
		std::cout << "vertex shader compiled: " << strFragment << "\n";
	}


	m_hProgramObject = glCreateProgram();
	glAttachShader(m_hProgramObject, m_hVertexShader);
	glAttachShader(m_hProgramObject, m_hFragmentShader);
	glLinkProgram(m_hProgramObject);
	glGetProgramiv(m_hProgramObject, GL_LINK_STATUS, &linked);
	if (!linked)
	{
		printProgramInfoLog();
		std::cout << "linking of shader failed...\n";
		std::cout << "*** Using fixed function pipeline ***\n";
		std::cout << "--------------------------------\n";
		return;
	}
	std::cout << "Link success...\n";
	std::cout << "--------------------------------\n";

	glUseProgram(m_hProgramObject); // turn on programmable pipeline
	//glUseProgram(0);
}

GLint CShader::GetVariable(string strVariable)
{
	// If we don't have an active program object, let's return -1
	if(!m_hProgramObject)
		return -1;

	// This returns the variable ID for a variable that is used to find
	// the address of that variable in memory.
	return glGetUniformLocation(m_hProgramObject, strVariable.c_str());
}


void CShader::ReleaseShader()
{
	// If our vertex shader pointer is valid, free it
	if(m_hVertexShader)
	{
		glDetachShader(m_hProgramObject, m_hVertexShader);
		glDeleteShader(m_hVertexShader);
		m_hVertexShader = NULL;
	}

	// If our fragment shader pointer is valid, free it
	if(m_hFragmentShader)
	{
		glDetachShader(m_hProgramObject, m_hFragmentShader);
		glDeleteShader(m_hFragmentShader);
		m_hFragmentShader = NULL;
	}

	// If our program object pointer is valid, free it
	if(m_hProgramObject)
	{
		glDeleteProgram(m_hProgramObject);
		m_hProgramObject = NULL;
	}
}

void CShader::printShaderInfoLog(unsigned int shader)
{
	int infologLen = 0;
	int charsWritten = 0;
	GLchar* infoLog;

	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLen);

	if (infologLen > 0)
	{
		infoLog = (GLchar*) malloc(infologLen);
		if (infoLog == NULL)
		{
			std::cout << "ERROR: Could not allocate infolog buffer" << std::endl;
			exit(1);
		}
		glGetShaderInfoLog(shader, infologLen, &charsWritten, infoLog);
		printf("InfoLog shader:\n%s\n", infoLog);
	}
}


void CShader::printProgramInfoLog()
{
	int infologLen = 0;
	int charsWritten = 0;
	GLchar* infoLog;

	glGetProgramiv(m_hProgramObject, GL_INFO_LOG_LENGTH, &infologLen);

	if (infologLen > 0)
	{
		infoLog = (GLchar*) malloc(infologLen);
		if (infoLog == NULL)
		{
			std::cout << "ERROR: Could not allocate infolog buffer" << std::endl;
			exit(1);
		}
		glGetProgramInfoLog(m_hProgramObject, infologLen, &charsWritten, infoLog);
		printf("InfoLog program:\n%s\n", infoLog);
	}
}